import type {
	PropOrGetter,
	FieldGetter,
	CacheMap,
} from '../../type';
import type {
	BaseType,
	PlainObject,
	PlainArray,
	CopyParamDest,
	CopyParamSrc,
	GetConverterReturn,
	DefaultTemplateOption,
	InnerDefaultTemplateOption,
	TemplateNewKeyFn,
	WidthDescribe,
	WidthLimitType,
	FrInfoObject,
	InfoObject,
	CalcColumnWidthFn,
	TreeNodeWrap,
	TreeNodeCallback,
	DebounceOption,
	DebounceExtraProp,
	ThrottleOption,
	CompareFn,
} from './type';
import {numberToString} from '../math';
import {pad} from '../format';
//region isBaseType
/**
 * 判断基本类型(可以直接赋值)
 * null, undefined, boolean, number, string, function, bigint, symbol皆属于基本类型, 可以直接赋值
 * @param val	测试值
 * @return		测试结果
 */
function isBaseType(val:unknown):val is BaseType{
	return val==null||
		typeof val==='boolean'||
		typeof val==='number'||
		typeof val==='string'||
		typeof val==='function'||
		typeof val==='bigint'||
		typeof val==='symbol';
}
//endregion
//region isObject
/**
 * 判断普通对象(不包括数组)
 * 除了isBaseType判断为true和数组的值
 * @param val	测试值
 * @return		测试结果
 */
function isObject(val:unknown):val is PlainObject{
	return val!=null&&typeof val==='object'&&!Array.isArray(val);
}
//endregion
//region copy方法相关
/**
 * copy方法, 数组对位替换
 */
class CopyReplace<E>{
	public value:Array<E>;
	constructor(value:Array<E>){
		this.value=value;
	}
}
function copyReplace<E>(value:Array<E>){
	return new CopyReplace<E>(value);
}
/**
 * copy方法, 数组或对象直接覆盖
 */
class CopyCover{
	public value;
	constructor(value:unknown){
		this.value=value;
	}
}
function copyCover(value:unknown){
	return new CopyCover(value);
}
/**
 * 深度拷贝, 支持: Date, RegExp, Set, Map等类型的拷贝
 * dest,src要么同为数组, 要么同为对象
 * @param dest	目标对象或数组, 会被改变
 * @param src	被拷贝的对象或数组
 */
function copy<D extends CopyParamDest>(dest:D,src:CopyParamSrc<D,CopyReplace<unknown>>):void{
	if(Array.isArray(src)){
		const len=(dest as PlainArray).length;
		//数组新增模式
		for(let i=0;i<src.length;++i){
			_copyInner(dest,src[i],len+i);
		}
	}else if(src.constructor===CopyReplace){
		//数组替换模式
		const array=src.value;
		for(let i=0;i<array.length;++i){
			_copyInner(dest,array[i],i);
		}
	}else{
		// dest,src都是对象
		for(const key of Object.keys(src)){
			// @ts-ignore
			_copyInner(dest,src[key],key);
		}
	}
}
/**
 * 替换指定键的值
 * @param dest	目标对象或数组, 会被改变
 * @param value	用于替换的值
 * @param key	被替换的键
 */
function _copyInner(dest:CopyParamDest,value:unknown,key:number|string):void{
	let valConstructor;
	if(isBaseType(value)){
		// @ts-ignore
		dest[key]=value;
	}else if(value instanceof Date){
		// @ts-ignore
		dest[key]=new Date(value);
	}else if(value instanceof RegExp){
		// @ts-ignore
		const regexp=new RegExp(value);
		regexp.lastIndex=value.lastIndex;
		// @ts-ignore
		dest[key]=regexp;
	}else if(value instanceof Set||value instanceof Map){
		// @ts-ignore
		dest[key]=new value.constructor(value);
		// @ts-ignore
	}else if((valConstructor=value.constructor)===CopyCover){
		// @ts-ignore
		dest[key]=value.value;
	}else{
		// 被拷贝值为数组或对象
		// @ts-ignore
		const oldValue=dest[key];
		if(
			isBaseType(oldValue)||
			(
				// @ts-ignore
				(Array.isArray(value)||valConstructor===CopyReplace)!==Array.isArray(oldValue)
			)
		){
			/**
			 * 当以下情况时丢弃原有值:
			 * 1.原有值为基本类型
			 * 2.原有值和被拷贝值,类型不相同时
			 */
			// @ts-ignore
			dest[key]=valConstructor===CopyReplace?[]:new value.constructor();
		}
		// @ts-ignore
		return copy(dest[key],value);
	}
}
//endregion
//region extend
/**
 * 深度拷贝
 * 合并多个对象(深度拷贝), 后面覆盖前面, 会改变第一个对象, 支持: Date, RegExp, Set, Map等类型的拷贝
 * @param target	目标对象或数组, 会被改变
 * @param srcs		被拷贝的对象或数组
 * @return			第一个参数
 */
function extend<D extends CopyParamDest,S extends CopyParamSrc<D,CopyReplace<unknown>>>(target:D,...srcs:Array<S>):CopyParamSrc<D,never>{
	for(const src of srcs){
		copy(target,src);
	}
	// @ts-ignore
	return target;
}
//endregion
//region getConverter
/**
 * 返回转换为指定实例的类型的转换器(null、undefined或不能转换时取传入的默认值)
 * @param type	目标类型的实例
 * @return		转换器
 */
function getConverter<T>(type:T):GetConverterReturn<T>{
	if(typeof type==='boolean'){
		// @ts-ignore
		return function(val:unknown):boolean{
			if(typeof val==='string'&&(val.length===5||val.length===4)){
				const val2=val.toLowerCase();
				if(val2==='false'){
					return false;
				}else if(val2==='true'){
					return true;
				}
			}
			if(typeof val==='boolean'){
				return val;
			}
			return type;
		};
	}else if(typeof type==='number'){
		// @ts-ignore
		return function(val:unknown):number{
			if(typeof val==='string'&&val){
				val=Number(val);
			}
			if(typeof val==='number'&&Number.isFinite(val)){
				return val;
			}
			return type;
		};
	}else if(typeof type==='string'){
		// @ts-ignore
		return function(val:unknown):string{
			if(val!=null){
				return val+'';
			}
			return type;
		};
	}else{
		// @ts-ignore
		return function(val:unknown):unknown{
			return val;
		};
	}
}
//endregion
//region defaultTemplate方法相关
/**
 * defaultTemplate方法, 更改对象的键名
 */
class TemplateRename{
	public key:string|TemplateNewKeyFn;
	public def:any;
	constructor(key:string|TemplateNewKeyFn,def:any){
		this.key=key;
		this.def=def;
	}
}
function templateRename(key:string|TemplateNewKeyFn,def:any){
	return new TemplateRename(key,def);
}
/**
 * defaultTemplate方法, 获取真正的 key 和 def
 */
function _setObjectKey(obj:any,src:any,opt:InnerDefaultTemplateOption,owner:any,def:any,key:any):void{
	if(def!=null&&def.constructor===TemplateRename){
		key=typeof def.key==='function'?def.key(key):def.key;
		def=def.def;
	}
	const value=_defaultTemplate(def,src,opt,owner);
	if(value!==undefined){
		obj[key]=value;
	}
}
/**
 * 默认值模板
 * 定义src的默认值(当提供的数据为null或undefined则启用默认值), 且自动转换为默认值的类型.
 * 会删除原始数据有且模板中不存在的属性, 添加模板上有且原始数据不存在的属性.
 * 对象计算后值为 undefined 的键会被忽略
 * @param def	模板, null表示原样返回, 数组中的对象表示对每个元素的模板, 对象中的 " ? " 键表示适配所有键
 * @param src	原数据
 * @param opt	选项
 * @param opt.allTemplateKey	Object的通用模板键
 * @return 处理后的数据
 */
function defaultTemplate(def:any,src:any,opt:DefaultTemplateOption={}):any{
	return _defaultTemplate(def,src,{
		objectFn:opt.allTemplateKey
			//region 启用对象通用模板
			?function(allTemplateKey:string,def:any,src:any,opt:InnerDefaultTemplateOption){
				const obj:PlainObject={};
				const defKeys=Object.keys(def);
				if(isObject(src)){
					if(allTemplateKey in def){
						const allTemplateDef=def[allTemplateKey];
						for(const key of Object.keys(src)){
							_setObjectKey(obj,src[key],opt,src,key in def?def[key]:allTemplateDef,key);
						}
						for(const key of defKeys){
							if(key===allTemplateKey||Object.prototype.hasOwnProperty.call(src,key)) continue;
							_setObjectKey(obj,undefined,opt,src,def[key],key);
						}
					}else{
						for(const key of defKeys){
							_setObjectKey(obj,src[key],opt,src,def[key],key);
						}
					}
				}else{
					for(const key of defKeys){
						if(key===allTemplateKey) continue;
						_setObjectKey(obj,undefined,opt,src,def[key],key);
					}
				}
				return obj;
			}.bind(null,opt.allTemplateKey)
			//endregion
			//region 禁用对象通用模板
			:function(def:any,src:any,opt:any){
				const obj:PlainObject={};
				for(const key of Object.keys(def)){
					_setObjectKey(obj,src==null||Array.isArray(src)?undefined:src[key],opt,src,def[key],key);
				}
				return obj;
			},
		//endregion
	},undefined);
}
function _defaultTemplate(def:any,src:any,opt:InnerDefaultTemplateOption,owner:any):any{
	if(def==null){
		return src;
	}else if(typeof def==='function'){
		return def(src,owner);
	}else if(isBaseType(def)){
		return src!=null&&isBaseType(src)?getConverter(def)(src):def;
	}else if(Array.isArray(def)){
		if(Array.isArray(src)){
			if(def.length){
				const arr=new Array(src.length);
				const def1=def[0];
				for(let i=0;i<src.length;++i){
					arr[i]=_defaultTemplate(def1,src[i],opt,src);
				}
				return arr;
			}
			return src;
		}
		return [];
	}else{
		return opt.objectFn(def,src,opt);
	}
}
//endregion
//region toPlainString
/**
 * 把值转换为字符串(可直接eval的字符串)
 * @param value	任意值
 * @return		值的字符串表示
 */
function toPlainString(value:any):string{
	if(value!=null&&typeof value==='object'){
		if(Array.isArray(value)){
			return '['+value.map((ele) => {
				return toPlainString(ele);
			}).join()+']';
		}else{
			return '{'+Object.keys(value).map((key) => {
				return key+':'+toPlainString(value[key]);
			}).join()+'}';
		}
	}else if(typeof value==='string'){
		return JSON.stringify(value);
	}else{
		return value+'';
	}
}
//endregion
//region storageSet
/**
 * 存储数据
 * 向localStorage存储数据
 * @param key	存储的键名
 * @param value	需要存储的数据
 */
function storageSet(key:string,value:any):void{
	localStorage.setItem(key,'"use strict";return '+toPlainString(value));
}
//endregion
//region storageGet
/**
 * 获取数据
 * 向localStorage获取已经存储的数据
 * @param key	存储的键名
 * @return		存储的数据
 */
function storageGet(key:string):any{
	const val=localStorage.getItem(key);
	if(val==null) return null;
	return Function(val)();
}
//endregion
//region storageDel
/**
 * 删除数据
 * 向localStorage删除已经存储的数据
 * @param key	存储的键名
 */
function storageDel(key:string):void{
	localStorage.removeItem(key);
}
//endregion
//region getCalcColumnWidthFn
/**
 * 列宽表达式(返回宽度列表生成器)
 * 通过列宽表达式列表, 返回宽度列表生成器, 表达式中, 数字表示固定宽度px, fr表示剩余宽度的权重, 支持百分数, 加减乘除运算
 * @param columnWidths 列宽表达式
 * @return 宽度列表生成器
 */
function getCalcColumnWidthFn(columnWidths:Array<WidthDescribe>):CalcColumnWidthFn{
	//有一列宽度被限制
	let widthLimitChange:boolean;
	//除去固定宽度, 剩余的宽度
	let restWidth:number;
	//剩下的fr数量
	let curFrCount:number;
	const frArr:Array<FrInfoObject>=[];
	let getFnsStr='return {';
	let allSumStr='0';
	//region _widthFn宽度计算
	function _widthFn(this:InfoObject,allWidth:number,frwidth:number):number{
		let realWidth=this.expr(allWidth,frwidth);
		let limit:WidthLimitType=false;
		realWidth=Math.round(realWidth)||0;
		if(realWidth<this.minWidth){
			realWidth=this.minWidth;
			limit='min';
		}
		if(this.maxWidth!=null&&realWidth>this.maxWidth){
			realWidth=this.maxWidth;
			limit='max';
		}
		if(this.hasFr&&limit!==this.limit){
			if(this.lastFixWidth===undefined){
				this.initFixWidth=this.lastFixWidth=Math.round(this.expr(allWidth,0));
			}
			if(limit){
				restWidth+=this.lastFixWidth-realWidth;
				this.lastFixWidth=realWidth;
				if(!this.limit){
					curFrCount-=this.count;
				}
			}else{
				restWidth+=this.lastFixWidth-this.initFixWidth!;
				this.lastFixWidth=this.initFixWidth!;
				curFrCount+=this.count;
			}
			this.limit=limit;
			widthLimitChange=true;
		}
		return realWidth;
	}
	//endregion
	//region _strArgFn处理字符串参数
	function _strArgFn(str:string|undefined,index:number,minWidth?:number,maxWidth?:number):InfoObject{
		const expr=(str||'1fr').replace(/(\d+(?:\.\d+)?)(?:%|(fr))/g,'$1*$2width');
		getFnsStr+=index+':function(allWidth,frwidth){var width=allWidth/100;return '+expr+'},';
		allSumStr+='+Math.round('+expr+')';
		const hasFr=expr.indexOf('fr')!==-1;
		const obj={
			width:_widthFn,
			hasFr,
			minWidth:minWidth!=null&&minWidth>0?Math.round(minWidth):0,
			maxWidth:maxWidth!=null&&maxWidth>0?Math.round(maxWidth):null,
			index,
		} as InfoObject;
		if(obj.hasFr){
			frArr.push(obj);
		}
		return obj;
	}
	//endregion
	//region _numArgFn处理数字参数
	function _numArgFn(width:number,minWidth?:number,maxWidth?:number):number{
		let realWidth=width>0?width:0;
		if(minWidth!=null&&realWidth<minWidth){
			realWidth=minWidth;
		}
		if(maxWidth!=null&&maxWidth>0&&realWidth>maxWidth){
			realWidth=maxWidth;
		}
		realWidth=Math.round(realWidth);
		allSumStr+='+'+realWidth;
		return realWidth;
	}
	//endregion
	//region 循环处理参数
	const widthObjs=columnWidths.map((widthArg,index) => {
		if(typeof widthArg==='number'){
			return _numArgFn(widthArg);
		}else if(typeof widthArg==='string'){
			return _strArgFn(widthArg,index);
		}else{
			const {width}=widthArg;
			if(typeof width==='number'){
				return _numArgFn(width,widthArg.minWidth,widthArg.maxWidth);
			}else{
				return _strArgFn(width,index,widthArg.minWidth,widthArg.maxWidth);
			}
		}
	});
	//endregion;
	const allFnObj=Function(getFnsStr+'allSum:function(allWidth,frwidth){var width=allWidth/100;return '+allSumStr+'}}')();
	const allSum=allFnObj.allSum;
	let frCount=0;
	for(let i=0;i<widthObjs.length;++i){
		const obj=widthObjs[i];
		if(typeof obj!=='number'){
			obj.expr=allFnObj[i];
			if(obj.hasFr){
				obj.count=obj.expr(0,1)-obj.expr(0,0);
				frCount+=obj.count;
			}
		}
	}
	//region 宽度列表生成器
	return function(width){
		widthLimitChange=false;
		curFrCount=frCount;
		width=Math.round(width);
		//region 计算剩余宽度remain
		let remain:number;
		if(curFrCount){
			restWidth=width-allSum(width,0);
			remain=restWidth/curFrCount;
			remain=remain>0?remain:0;
		}else{
			remain=0;
		}
		//endregion
		//region 初次计算宽度列表
		const realColumnWidths=new Array<number>(widthObjs.length);
		for(let i=0;i<widthObjs.length;++i){
			const obj=widthObjs[i];
			if(typeof obj==='number'){
				realColumnWidths[i]=obj;
			}else{
				if(obj.hasFr){
					obj.limit=false;
					obj.lastFixWidth=undefined;
				}
				realColumnWidths[i]=obj.width(width,remain);
			}
		}
		//endregion
		while(widthLimitChange){
			widthLimitChange=false;
			if(curFrCount){
				remain=restWidth/curFrCount;
				remain=remain>0?remain:0;
				for(let i=0;i<frArr.length;++i){
					const obj=frArr[i];
					realColumnWidths[obj.index]=obj.width(width,remain);
				}
			}else{
				remain=0;
			}
		}
		let miss=width-realColumnWidths.reduce(function(a,b){
			return a+b;
		},0);
		//region 有剩余宽度,且计算总宽度和传入宽度不一致(需要抹平)
		if(remain&&miss){
			const step=miss>0?1:-1;
			let cur=0;
			let disable=0;
			while(miss&&frArr.length){
				const {limit,minWidth,maxWidth,index}=frArr[cur];
				if(!limit){
					const width=realColumnWidths[index]+step;
					if(minWidth>width){
						frArr[cur].limit='min';
						++disable;
					}else if(maxWidth!=null&&maxWidth<width){
						frArr[cur].limit='max';
						++disable;
					}else{
						realColumnWidths[index]=width;
						miss-=step;
					}
				}else{
					++disable;
				}
				++cur;
				if(cur===frArr.length){
					if(disable===frArr.length){
						break;
					}
					cur=0;
					disable=0;
				}
			}
		}
		//endregion
		return realColumnWidths;
	};
	//endregion
}
//endregion
//region loopTree
/**
 * 遍历树结构
 * 遍历树结构, 可提前退出, 可检索祖先节点
 * @param tree 				需要遍历的树
 * @param callback			每个节点的回调函数, 返回值可以指示, 删除节点, 跳过节点, 终止遍历
 * @param [childrenAttr='children']	节点的子节点数组属性
 * @return 被修改后的树
 */
function loopTree(tree:Array<any>,callback:TreeNodeCallback,childrenAttr='children'):Array<any>{
	const standardTree={[childrenAttr]:tree};
	_loopTree(tree,callback,childrenAttr,standardTree);
	return standardTree[childrenAttr];
}
/**
 * 遍历树结构(内部)
 * @param tree			需要遍历的树
 * @param callback		每个节点的回调函数, 返回值可以指示, 删除节点, 跳过节点, 终止遍历
 * @param childrenAttr	节点的子节点数组属性
 * @param standardTree	树(用于修改)
 * @param [parent]		父节点包装对象
 * @return 是否提前退出遍历
 */
function _loopTree(
	tree:Array<any>,
	callback:TreeNodeCallback,
	childrenAttr:string,
	standardTree:{[k:string]:Array<any>},
	parent?:TreeNodeWrap
):boolean{
	let hasRemove=false;
	let hasStop=false;
	for(let i=0;i<tree.length;++i){
		const node=tree[i];
		const nodeWrap={node,parent};
		const state=callback(nodeWrap);
		if(state){
			if(state===true){
				hasStop=true;
				break;
			}else{
				if(state.remove){
					tree[i]=null;
					hasRemove=true;
				}
				if(state.stop){
					hasStop=true;
					break;
				}else if(state.skip||state.remove){
					continue;
				}
			}
		}
		const children=node[childrenAttr];
		if(children&&_loopTree(children,callback,childrenAttr,node,nodeWrap)){
			hasStop=true;
			break;
		}
	}
	if(hasRemove){
		standardTree[childrenAttr]=tree.filter((n) => n!==null);
	}
	return hasStop;
}
//endregion
//region getUid
/**
 * 获取唯一键
 * 获取和当前时间相关的唯一键
 * @return 唯一键
 */
function getUid():string{
	return pad(numberToString(Math.floor(Math.random()*(64*64*64-1))),3)+numberToString(Date.now());
}
//endregion
//region copyToClipboard
/**
 * 复制到剪贴板降级方式
 * @param text	复制的文本
 * @return 是否成功
 */
function _copyToClipboardFallback(text:string):Promise<void>{
	return new Promise((resolve,reject) => {
		const area=document.createElement('textarea');
		area.value=text;
		area.contentEditable='true';
		area.style.cssText='position:fixed;left:-100%;top:-100%;';
		document.body.appendChild(area);
		area.focus();
		area.select();
		const res=document.execCommand('copy');
		document.body.removeChild(area);
		if(res){
			resolve();
		}else{
			reject(res);
		}
	});
}
/**
 * 复制到剪贴板
 * @param text 复制的文本
 * @return {Promise<void>}
 */
function copyToClipboard(text:string):Promise<void>{
	return navigator.clipboard!==undefined
		?navigator.clipboard.writeText(text).catch(() => {
			return _copyToClipboardFallback(text);
		})
		:_copyToClipboardFallback(text);
}
//endregion
//region waitTime
/**
 * 等待指定毫秒数的Promise
 * @param [time]	毫秒数
 * @return Promise
 */
function waitTime(time?:number):Promise<void>{
	return new Promise<void>(function(resolve){
		if(time==null){
			// @ts-ignore
			self.requestAnimationFrame(resolve);
		}else{
			self.setTimeout(resolve,time);
		}
	});
}
//endregion
//region debounce
/**
 * 创建防抖函数(wait为null或0时, 只支持trailing, maxWait失效, 且为节流函数)
 * @param func		需要防抖的函数
 * @param [wait]	延时毫秒
 * @param [options]	选项
 * @return 防抖函数
 */
function debounce<T extends((...args:Array<any>) => unknown)>(func:T,wait?:number|null,options?:DebounceOption):T&DebounceExtraProp<T>{
	//region 执行参数
	let lastArgs:any;
	let lastThis:any;
	let result:any;
	let timerId:number|undefined;
	let lastCallTime:number|undefined;
	let lastInvokeTime=0;
	//endregion
	const useRAF:boolean=wait==null;
	const waitNum:number=wait||0;
	const leading:boolean=waitNum!==0&&(options&&options.leading)===true;
	const trailing:boolean=(options&&options.trailing)!==false;
	const maxWait=options&&('maxWait' in options)?Math.max(waitNum,options.maxWait||0):undefined;
	//region startTimer设置定时器
	function startTimer(pendingFunc:() => void,waitNum:number){
		if(useRAF){
			return requestAnimationFrame(pendingFunc);
		}
		return self.setTimeout(pendingFunc,waitNum);
	}
	//endregion
	//region cancelTimer关闭定时器
	function cancelTimer(id:number):void{
		if(useRAF){
			return self.cancelAnimationFrame(id);
		}else{
			return self.clearTimeout(id);
		}
	}
	//endregion
	//region shouldInvoke是否应该执行
	function shouldInvoke(time:number):false|'ready'|'max'{
		const sinceLastInvoke=time-lastInvokeTime;
		if((lastCallTime===undefined||time-lastCallTime>=waitNum)&&sinceLastInvoke>=waitNum){
			return 'ready';
		}else if(maxWait!==undefined&&sinceLastInvoke>=maxWait){
			return 'max';
		}
		return false;
	}
	//endregion
	//region remainingWait剩余需要等待的时间
	function remainingWait(time:number){
		const timeWaiting=waitNum-(time-lastCallTime!);
		return maxWait!==undefined
			?Math.min(timeWaiting,maxWait-(time-lastInvokeTime))
			:timeWaiting;
	}
	//endregion
	//region timerExpired判断执行或者延时执行
	function timerExpired(){
		const time=Date.now();
		if(shouldInvoke(time)){
			return trailingEdge(time);
		}else{
			timerId=startTimer(timerExpired,remainingWait(time));
		}
	}
	//endregion
	//region leadingEdge开头调用
	function leadingEdge(time:number){
		lastInvokeTime=time;
		timerId=startTimer(timerExpired,waitNum);
		return leading?invokeFunc(time):result;
	}
	//endregion
	//region trailingEdge结尾调用
	function trailingEdge(time:number){
		timerId=undefined;
		if(trailing&&lastArgs){
			return invokeFunc(time);
		}else{
			lastArgs=lastThis=undefined;
		}
		return result;
	}
	//endregion
	//region invokeFunc执行方法
	function invokeFunc(time:number){
		const args=lastArgs;
		const thisArg=lastThis;
		lastArgs=lastThis=undefined;
		lastInvokeTime=time;
		result=func.apply(thisArg,args);
		return result;
	}
	//endregion
	const debounced=function(this:any,...args){
		const time=Date.now();
		const isInvoking=shouldInvoke(time);
		lastArgs=args;
		lastThis=this;
		lastCallTime=time;
		if(isInvoking==='ready'){
			if(timerId===undefined){
				return leadingEdge(time);
			}
		}else if(isInvoking==='max'){
			return invokeFunc(time);
		}else if(timerId===undefined){
			timerId=startTimer(timerExpired,waitNum);
		}
		return result;
	} as T;
	//region 添加额外的方法cancel, flush, pending
	return Object.assign(debounced,{
		cancel():void{
			if(timerId!==undefined){
				cancelTimer(timerId);
			}
			lastInvokeTime=0;
			lastArgs=lastThis=timerId=lastCallTime=undefined;
		},
		flush():ReturnType<T>{
			if(timerId!==undefined){
				cancelTimer(timerId);
				return trailingEdge(Date.now());
			}else{
				return result;
			}
		},
		pending():boolean{
			return timerId!==undefined;
		},
	});
	//endregion
}
//endregion
//region throttle
/**
 * 创建节流函数(wait为null或0时, 只支持trailing)
 * @param func		需要节流的函数
 * @param [wait]	间隔时间
 * @param [options]	选项
 * @return 节流函数
 */
function throttle<T extends((...args:Array<any>) => unknown)>(func:T,wait?:number,options?:ThrottleOption):T&DebounceExtraProp<T>{
	return debounce(func,wait,Object.assign({
		leading:true,
		trailing:true,
	},options,{
		maxWait:wait,
	}));
}
//endregion
//region insertionSort
/**
 * 插入排序
 * @param array			排序的数组
 * @param compareFn		排序方法
 * @return 排序后的原数组
 */
function insertionSort<T>(array:Array<T>,compareFn:CompareFn<T>):Array<T>{
	for(let i=1;i<array.length;++i){
		const ele=array[i];
		let j=i-1;
		while(j>=0&&compareFn(array[j],ele)>0){
			array[j+1]=array[j];
			--j;
		}
		array[j+1]=ele;
	}
	return array;
}
//endregion
//region mergeSort
/**
 * 归并排序
 * @param array			排序的数组
 * @param compareFn		排序方法
 * @return 排序后的原数组
 */
function mergeSort<T>(array:Array<T>,compareFn:CompareFn<T>):Array<T>{
	const len=array.length;
	if(len>8){
		const mid=Math.floor(len/2);
		_merge(
			array,
			mergeSort(array.slice(0,mid),compareFn),
			mergeSort(array.slice(mid),compareFn),
			compareFn
		);
	}else if(len>1){
		insertionSort(array,compareFn);
	}
	return array;
}
function _merge<T>(array:Array<T>,left:Array<T>,right:Array<T>,compareFn:CompareFn<T>):void{
	const len=array.length;
	const len1=left.length;
	const len2=right.length;
	let i=0;
	let j=0;
	while(i+j<len){
		array[i+j]=
			i>=len1
				?right[j++]
				:j>=len2
					?left[i++]
					:compareFn(left[i],right[j])<=0
						?left[i++]
						:right[j++];
	}
}
//endregion
//region operateDom
const _cacheFns=new Set<() => void>();
const _starterFn=throttle(() => {
	for(const fn of _cacheFns){
		fn();
	}
	_cacheFns.clear();
});
/**
 * 执行操作dom的方法(会收集所有的操作到下一个动画帧执行)
 * @param callback	操作dom的方法
 */
function operateDom(callback:() => void):() => void{
	return function(){
		_cacheFns.delete(callback);
		_cacheFns.add(callback);
		_starterFn();
	};
}
//endregion
//region perfMarkFn
/**
 * 测量方法的执行时间
 * @param name	测量名
 * @param func	方法
 * @param log	是否使用 console.time 测量
 */
function perfMarkFn<T extends((...args:Array<any>) => unknown)>(name:string,func:T,log?:boolean):T{
	let startFn:() => void;
	let endFn:() => void;
	if(log){
		startFn=() => {
			console.time(name);
		};
		endFn=() => {
			console.timeEnd(name);
		};
	}else{
		const start=`${name}-start`;
		const end=`${name}-end`;
		startFn=() => {
			performance.mark(start);
		};
		endFn=() => {
			performance.mark(end);
			performance.measure(name,start,end);
			performance.clearMarks(start);
			performance.clearMarks(end);
		};
	}
	return function(...args){
		startFn();
		const result=func(...args);
		if(result instanceof Promise){
			return result.finally(endFn);
		}
		endFn();
		return result;
	} as T;
}
//endregion
//region bindEvent
/**
 * 绑定一个全局事件, 返回注册函数和注销函数
 * @param type		事件名
 * @param listener	回调函数
 * @param options	选项
 */
function bindEvent<K extends keyof WindowEventMap>(
	type:K,
	listener:(this:Window,ev:WindowEventMap[K]) => any,
	options?:boolean|AddEventListenerOptions,
):[() => void,() => void];
/**
 * 绑定多个全局事件, 返回注册函数和注销函数
 * @param objs		多个事件名及对应的回调函数
 * @param options	选项
 */
function bindEvent<K extends keyof WindowEventMap>(
	objs:{
		type:K,
		listener:(this:Window,ev:WindowEventMap[K]) => any,
	}[],
	options?:boolean|AddEventListenerOptions,
):[() => void,() => void];
function bindEvent<
	K extends keyof WindowEventMap,
	L extends((this:Window,ev:WindowEventMap[K]) => any),
	O extends boolean|AddEventListenerOptions,
>(
	typeOrArr:K|{
		type:K,
		listener:L,
	}[],
	listenerOrOptions?:L|O,
	options?:O,
):[() => void,() => void]{
	if(Array.isArray(typeOrArr)){
		return [
			() => {
				for(const {type,listener} of typeOrArr){
					window.addEventListener(type,listener,listenerOrOptions as O);
				}
			},
			() => {
				for(const {type,listener} of typeOrArr){
					window.removeEventListener(type,listener,listenerOrOptions as O);
				}
			},
		];
	}
	return [
		() => {
			window.addEventListener(typeOrArr,listenerOrOptions as L,options);
		},
		() => {
			window.removeEventListener(typeOrArr,listenerOrOptions as L,options);
		},
	];
}
//endregion
//region 创建键值对缓冲区
function createCache<K,V>():CacheMap<K,V>{
	const cache=new Map<K,V>();
	const get:(key:K) => V|undefined=(key) => {
		return cache.get(key);
	};
	const set:(key:K,value:V) => void=(key,value) => {
		cache.set(key,value);
	};
	const values:() => V[]=() => {
		const arr=new Array(cache.size);
		let i=0;
		for(const val of cache.values()){
			arr[i++]=val;
		}
		return arr;
	};
	const clear:() => void=() => {
		cache.clear();
	};
	return {
		get,
		set,
		values,
		clear,
	};
}
//endregion
//region createGetter
function createGetter<D,R>(propOrGetter:PropOrGetter<D,R>):FieldGetter<D,R>{
	if(typeof propOrGetter==='string'){
		// @ts-ignore
		return (data) => {
			return data[propOrGetter];
		};
	}
	return propOrGetter;
}
//endregion
export {
	isBaseType,
	isObject,
	copyReplace,
	copyCover,
	copy,
	extend,
	getConverter,
	templateRename,
	defaultTemplate,
	toPlainString,
	storageSet,
	storageGet,
	storageDel,
	getCalcColumnWidthFn,
	loopTree,
	getUid,
	copyToClipboard,
	waitTime,
	debounce,
	throttle,
	insertionSort,
	mergeSort,
	operateDom,
	perfMarkFn,
	bindEvent,
	createCache,
	createGetter,
};